[libvirt] [PATCH v2 0/5] AppArmor support for lxc containers

This is a repost of the previous patch series, with the following changes: * Dropped the patch setting "none" security driver as default in lxc.conf * Add a patch to implement support for "none" type with apparmor security model. Cédric Bosdonnat (5): LXC driver: generate apparmor profiles for guests Make sure apparmor is started before libvirtd apparmor: add debug traces when changing profile. add support for apparmor in lxc-enter-namespace apparmor: handle "none" type daemon/libvirtd.service.in | 1 + examples/apparmor/Makefile.am | 2 ++ examples/apparmor/TEMPLATE | 2 +- examples/apparmor/libvirt-lxc | 17 ++++++++++ src/libvirt-lxc.c | 13 ++++++++ src/security/security_apparmor.c | 24 +++++++++++--- src/security/virt-aa-helper.c | 69 +++++++++++++++++++++++++++++----------- 7 files changed, 103 insertions(+), 25 deletions(-) create mode 100644 examples/apparmor/libvirt-lxc -- 1.8.5.2

From: Cédric Bosdonnat <cedric.bosdonnat@free.fr> use_apparmor() was first designed to be called from withing libvirtd, but libvirt_lxc also uses it. in libvirt_lxc, there is no need to check whether to use apparmor or not: just use it if possible. --- examples/apparmor/Makefile.am | 2 ++ examples/apparmor/TEMPLATE | 2 +- examples/apparmor/libvirt-lxc | 10 ++++++ src/security/security_apparmor.c | 10 +++--- src/security/virt-aa-helper.c | 69 +++++++++++++++++++++++++++++----------- 5 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 examples/apparmor/libvirt-lxc diff --git a/examples/apparmor/Makefile.am b/examples/apparmor/Makefile.am index 2630fef..a741e94 100644 --- a/examples/apparmor/Makefile.am +++ b/examples/apparmor/Makefile.am @@ -17,6 +17,7 @@ EXTRA_DIST= \ TEMPLATE \ libvirt-qemu \ + libvirt-lxc \ usr.lib.libvirt.virt-aa-helper \ usr.sbin.libvirtd @@ -30,6 +31,7 @@ apparmor_DATA = \ abstractionsdir = $(apparmordir)/abstractions abstractions_DATA = \ libvirt-qemu \ + libvirt-lxc \ $(NULL) templatesdir = $(apparmordir)/libvirt diff --git a/examples/apparmor/TEMPLATE b/examples/apparmor/TEMPLATE index 008a221..187dec5 100644 --- a/examples/apparmor/TEMPLATE +++ b/examples/apparmor/TEMPLATE @@ -5,5 +5,5 @@ #include <tunables/global> profile LIBVIRT_TEMPLATE { - #include <abstractions/libvirt-qemu> + #include <abstractions/libvirt-driver> } diff --git a/examples/apparmor/libvirt-lxc b/examples/apparmor/libvirt-lxc new file mode 100644 index 0000000..47f27b1 --- /dev/null +++ b/examples/apparmor/libvirt-lxc @@ -0,0 +1,10 @@ +# Last Modified: Fri Feb 7 13:01:36 2014 + + #include <abstractions/base> + + /usr/sbin/cron PUx, + /usr/lib/systemd/systemd PUx, + + /usr/lib/libsystemd-*.so.* mr, + /usr/lib/libudev-*.so.* mr, + /etc/ld.so.cache mr, diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index 257e3dd..14dc707 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -246,6 +246,11 @@ use_apparmor(void) return rc; } + /* If libvirt_lxc is calling us, then consider apparmor is used + * and enforced. */ + if (strstr(libvirt_daemon, "libvirt_lxc")) + return 1; + if (access(APPARMOR_PROFILES_PATH, R_OK) != 0) goto cleanup; @@ -341,7 +346,7 @@ AppArmorSetSecuritySCSILabel(virSCSIDevicePtr dev ATTRIBUTE_UNUSED, /* Called on libvirtd startup to see if AppArmor is available */ static int -AppArmorSecurityManagerProbe(const char *virtDriver) +AppArmorSecurityManagerProbe(const char *virtDriver ATTRIBUTE_UNUSED) { char *template = NULL; int rc = SECURITY_DRIVER_DISABLE; @@ -349,9 +354,6 @@ AppArmorSecurityManagerProbe(const char *virtDriver) if (use_apparmor() < 0) return rc; - if (virtDriver && STREQ(virtDriver, "LXC")) - return rc; - /* see if template file exists */ if (virAsprintf(&template, "%s/TEMPLATE", APPARMOR_DIR "/libvirt") == -1) diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index b9282b4..e7f1359 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -328,18 +328,24 @@ update_include_file(const char *include_file, const char *included_files, */ static int create_profile(const char *profile, const char *profile_name, - const char *profile_files) + const char *profile_files, int virtType) { char *template; char *tcontent = NULL; char *pcontent = NULL; char *replace_name = NULL; char *replace_files = NULL; + char *replace_driver = NULL; const char *template_name = "\nprofile LIBVIRT_TEMPLATE"; const char *template_end = "\n}"; + const char *template_driver = "libvirt-driver"; int tlen, plen; int fd; int rc = -1; + const char *driver_name = "qemu"; + + if (virtType == VIR_DOMAIN_VIRT_LXC) + driver_name = "lxc"; if (virFileExists(profile)) { vah_error(NULL, 0, _("profile exists")); @@ -371,6 +377,11 @@ create_profile(const char *profile, const char *profile_name, goto clean_tcontent; } + if (strstr(tcontent, template_driver) == NULL) { + vah_error(NULL, 0, _("no replacement string in template")); + goto clean_tcontent; + } + /* '\nprofile <profile_name>\0' */ if (virAsprintfQuiet(&replace_name, "\nprofile %s", profile_name) == -1) { vah_error(NULL, 0, _("could not allocate memory for profile name")); @@ -378,14 +389,26 @@ create_profile(const char *profile, const char *profile_name, } /* '\n<profile_files>\n}\0' */ - if (virAsprintfQuiet(&replace_files, "\n%s\n}", profile_files) == -1) { + if ((virtType != VIR_DOMAIN_VIRT_LXC) && + virAsprintfQuiet(&replace_files, "\n%s\n}", profile_files) == -1) { vah_error(NULL, 0, _("could not allocate memory for profile files")); VIR_FREE(replace_name); goto clean_tcontent; } + /* 'libvirt-<driver_name>\0' */ + if (virAsprintfQuiet(&replace_driver, "libvirt-%s", driver_name) == -1) { + vah_error(NULL, 0, _("could not allocate memory for profile driver")); + VIR_FREE(replace_driver); + goto clean_tcontent; + } + plen = tlen + strlen(replace_name) - strlen(template_name) + - strlen(replace_files) - strlen(template_end) + 1; + strlen(replace_driver) - strlen(template_driver) + 1; + + if (virtType != VIR_DOMAIN_VIRT_LXC) + plen += strlen(replace_files) - strlen(template_end); + if (plen > MAX_FILE_LEN || plen < tlen) { vah_error(NULL, 0, _("invalid length for new profile")); goto clean_replace; @@ -398,10 +421,14 @@ create_profile(const char *profile, const char *profile_name, pcontent[0] = '\0'; strcpy(pcontent, tcontent); + if (replace_string(pcontent, plen, template_driver, replace_driver) < 0) + goto clean_all; + if (replace_string(pcontent, plen, template_name, replace_name) < 0) goto clean_all; - if (replace_string(pcontent, plen, template_end, replace_files) < 0) + if ((virtType != VIR_DOMAIN_VIRT_LXC) && + replace_string(pcontent, plen, template_end, replace_files) < 0) goto clean_all; /* write the file */ @@ -427,6 +454,7 @@ create_profile(const char *profile, const char *profile_name, clean_replace: VIR_FREE(replace_name); VIR_FREE(replace_files); + VIR_FREE(replace_driver); clean_tcontent: VIR_FREE(tcontent); end: @@ -666,8 +694,8 @@ caps_mockup(vahControl * ctl, const char *xmlStr) goto cleanup; ctl->hvm = virXPathString("string(./os/type[1])", ctxt); - if (!ctl->hvm || STRNEQ(ctl->hvm, "hvm")) { - vah_error(ctl, 0, _("os.type is not 'hvm'")); + if (!ctl->hvm) { + vah_error(ctl, 0, _("os.type is not defined")); goto cleanup; } arch = virXPathString("string(./os/type[1]/@arch)", ctxt); @@ -1234,18 +1262,20 @@ main(int argc, char **argv) if (vah_add_file(&buf, ctl->newfile, "rw") != 0) goto cleanup; } else { - virBufferAsprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n", - LOCALSTATEDIR, ctl->def->name); - virBufferAsprintf(&buf, " \"%s/lib/libvirt/**/%s.monitor\" rw,\n", - LOCALSTATEDIR, ctl->def->name); - virBufferAsprintf(&buf, " \"%s/run/libvirt/**/%s.pid\" rwk,\n", - LOCALSTATEDIR, ctl->def->name); - virBufferAsprintf(&buf, " \"/run/libvirt/**/%s.pid\" rwk,\n", - ctl->def->name); - virBufferAsprintf(&buf, " \"%s/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n", - LOCALSTATEDIR, ctl->def->name); - virBufferAsprintf(&buf, " \"/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n", - ctl->def->name); + if (ctl->def->virtType == VIR_DOMAIN_VIRT_QEMU) { + virBufferAsprintf(&buf, " \"%s/log/libvirt/**/%s.log\" w,\n", + LOCALSTATEDIR, ctl->def->name); + virBufferAsprintf(&buf, " \"%s/lib/libvirt/**/%s.monitor\" rw,\n", + LOCALSTATEDIR, ctl->def->name); + virBufferAsprintf(&buf, " \"%s/run/libvirt/**/%s.pid\" rwk,\n", + LOCALSTATEDIR, ctl->def->name); + virBufferAsprintf(&buf, " \"/run/libvirt/**/%s.pid\" rwk,\n", + ctl->def->name); + virBufferAsprintf(&buf, " \"%s/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n", + LOCALSTATEDIR, ctl->def->name); + virBufferAsprintf(&buf, " \"/run/libvirt/**/*.tunnelmigrate.dest.%s\" rw,\n", + ctl->def->name); + } if (ctl->files) virBufferAdd(&buf, ctl->files, -1); } @@ -1282,7 +1312,8 @@ main(int argc, char **argv) vah_info(ctl->uuid); vah_info(tmp); rc = 0; - } else if ((rc = create_profile(profile, ctl->uuid, tmp)) != 0) { + } else if ((rc = create_profile(profile, ctl->uuid, tmp, + ctl->def->virtType)) != 0) { vah_error(ctl, 0, _("could not create profile")); unlink(include_file); } -- 1.8.5.2

On Mon, Mar 03, 2014 at 11:26:42AM +0100, Cédric Bosdonnat wrote:
From: Cédric Bosdonnat <cedric.bosdonnat@free.fr>
use_apparmor() was first designed to be called from withing libvirtd, but libvirt_lxc also uses it. in libvirt_lxc, there is no need to check whether to use apparmor or not: just use it if possible. --- examples/apparmor/Makefile.am | 2 ++ examples/apparmor/TEMPLATE | 2 +- examples/apparmor/libvirt-lxc | 10 ++++++ src/security/security_apparmor.c | 10 +++--- src/security/virt-aa-helper.c | 69 +++++++++++++++++++++++++++++----------- 5 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 examples/apparmor/libvirt-lxc
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

If apparmor security driver is enabled in either qemu or lxc driver configuration and libvirtd starts before AppArmor, it will fail. --- daemon/libvirtd.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/daemon/libvirtd.service.in b/daemon/libvirtd.service.in index e1f2a07..086da36 100644 --- a/daemon/libvirtd.service.in +++ b/daemon/libvirtd.service.in @@ -9,6 +9,7 @@ Before=libvirt-guests.service After=network.target After=dbus.service After=iscsid.service +After=apparmor.service Documentation=man:libvirtd(8) Documentation=http://libvirt.org -- 1.8.5.2

On Mon, Mar 03, 2014 at 11:26:43AM +0100, Cédric Bosdonnat wrote:
If apparmor security driver is enabled in either qemu or lxc driver configuration and libvirtd starts before AppArmor, it will fail. --- daemon/libvirtd.service.in | 1 + 1 file changed, 1 insertion(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

The reason for these is that aa-status doesn't show the process using the profile as they are in another namespace. --- src/security/security_apparmor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index 14dc707..1c1b128 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -593,6 +593,7 @@ AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, goto cleanup; } + VIR_DEBUG("Changing AppArmor profile to %s", profile_name); if (aa_change_profile(profile_name) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("error calling aa_change_profile()")); @@ -618,6 +619,7 @@ AppArmorSetSecurityChildProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, { int rc = -1; char *profile_name = NULL; + char *cmd_str = NULL; virSecurityLabelDefPtr secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_APPARMOR_NAME); @@ -637,11 +639,14 @@ AppArmorSetSecurityChildProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, if ((profile_name = get_profile_name(def)) == NULL) goto cleanup; + cmd_str = virCommandToString(cmd); + VIR_DEBUG("Changing AppArmor profile to %s on %s", profile_name, cmd_str); virCommandSetAppArmorProfile(cmd, profile_name); rc = 0; cleanup: VIR_FREE(profile_name); + VIR_FREE(cmd_str); return rc; } -- 1.8.5.2

On Mon, Mar 03, 2014 at 11:26:44AM +0100, Cédric Bosdonnat wrote:
The reason for these is that aa-status doesn't show the process using the profile as they are in another namespace. --- src/security/security_apparmor.c | 5 +++++ 1 file changed, 5 insertions(+)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- examples/apparmor/libvirt-lxc | 7 +++++++ src/libvirt-lxc.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/examples/apparmor/libvirt-lxc b/examples/apparmor/libvirt-lxc index 47f27b1..d404328 100644 --- a/examples/apparmor/libvirt-lxc +++ b/examples/apparmor/libvirt-lxc @@ -2,6 +2,13 @@ #include <abstractions/base> + # Needed for lxc-enter-namespace + capability sys_admin, + capability sys_chroot, + + # Added for lxc-enter-namespace --cmd /bin/bash + /bin/bash PUx, + /usr/sbin/cron PUx, /usr/lib/systemd/systemd PUx, diff --git a/src/libvirt-lxc.c b/src/libvirt-lxc.c index 074809a..f10fafc 100644 --- a/src/libvirt-lxc.c +++ b/src/libvirt-lxc.c @@ -33,6 +33,9 @@ #ifdef WITH_SELINUX # include <selinux/selinux.h> #endif +#ifdef WITH_APPARMOR +# include <sys/apparmor.h> +#endif #define VIR_FROM_THIS VIR_FROM_NONE @@ -240,6 +243,16 @@ virDomainLxcEnterSecurityLabel(virSecurityModelPtr model, _("Support for SELinux is not enabled")); goto error; #endif + } else if (STREQ(model->model, "apparmor")) { +#ifdef WITH_APPARMOR + if (aa_change_profile(label->label) < 0) + virReportSystemError(errno, _("error changing profile to %s"), + label->label); +#else + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Support for AppArmor is not enabled")); + goto error; +#endif } else { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("Security model %s cannot be entered"), -- 1.8.5.2

On Mon, Mar 03, 2014 at 11:26:45AM +0100, Cédric Bosdonnat wrote:
--- examples/apparmor/libvirt-lxc | 7 +++++++ src/libvirt-lxc.c | 13 +++++++++++++ 2 files changed, 20 insertions(+)
diff --git a/src/libvirt-lxc.c b/src/libvirt-lxc.c index 074809a..f10fafc 100644 --- a/src/libvirt-lxc.c +++ b/src/libvirt-lxc.c
@@ -240,6 +243,16 @@ virDomainLxcEnterSecurityLabel(virSecurityModelPtr model, _("Support for SELinux is not enabled")); goto error; #endif + } else if (STREQ(model->model, "apparmor")) { +#ifdef WITH_APPARMOR + if (aa_change_profile(label->label) < 0) + virReportSystemError(errno, _("error changing profile to %s"), + label->label);
Missing 'goto error'
+#else + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Support for AppArmor is not enabled")); + goto error; +#endif } else { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("Security model %s cannot be entered"),
ACK, I'll fix the missing goto when pushing Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

--- src/security/security_apparmor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index 1c1b128..a74a91c 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -417,7 +417,8 @@ AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, if (!secdef) return -1; - if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) + if ((secdef->type == VIR_DOMAIN_SECLABEL_STATIC) || + (secdef->type == VIR_DOMAIN_SECLABEL_NONE)) return 0; if (secdef->baselabel) { @@ -580,6 +581,9 @@ AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, if (!secdef) return -1; + if (secdef->label == NULL) + return 0; + if ((profile_name = get_profile_name(def)) == NULL) return rc; @@ -626,6 +630,9 @@ AppArmorSetSecurityChildProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, if (!secdef) goto cleanup; + if (secdef->label == NULL) + return 0; + if (STRNEQ(SECURITY_APPARMOR_NAME, secdef->model)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " -- 1.8.5.2

On Mon, Mar 03, 2014 at 11:26:46AM +0100, Cédric Bosdonnat wrote:
--- src/security/security_apparmor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Mon, Mar 03, 2014 at 11:26:41AM +0100, Cédric Bosdonnat wrote:
This is a repost of the previous patch series, with the following changes: * Dropped the patch setting "none" security driver as default in lxc.conf * Add a patch to implement support for "none" type with apparmor security model.
Cédric Bosdonnat (5): LXC driver: generate apparmor profiles for guests Make sure apparmor is started before libvirtd apparmor: add debug traces when changing profile. add support for apparmor in lxc-enter-namespace apparmor: handle "none" type
This series is now pushed to GIT master Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Cédric Bosdonnat
-
Daniel P. Berrange